package info.kimjihyok.ripplelibrary;

import info.kimjihyok.ripplelibrary.util.Constant;
import info.kimjihyok.ripplelibrary.util.RectF;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Shader;
import ohos.agp.render.SweepShader;
import ohos.agp.utils.Color;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayAttributes;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.multimodalinput.event.TouchEvent;

import java.util.Optional;

import static info.kimjihyok.ripplelibrary.util.ColorConverUtils.colorToRgb;

public class ColorPicker extends Component implements Component.DrawTask, Component.TouchEventListener {
    private static final int[] COLORS = new int[]{0xFFFF0000, 0xFFFF00FF,
            0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 0xFFFFFF00, 0xFFFF0000};
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, 0xD000F00, VoiceRippleView.class.getSimpleName());
    /**
     * onColorSelectedListener
     */
    public OnColorSelectedListener onColorSelectedListener;
    private Paint mColorWheelPaint;
    private Paint mPointerHaloPaint;
    private Paint mPointerColor;
    private int mColorWheelRadius;
    private int mPreferredColorWheelRadius;
    private Paint mCenterOldPaint;
    private Paint mCenterNewPaint;
    private Paint mCenterHaloPaint;
    private float mAngle;
    private int mCenterOldColor;
    private boolean isShowCenterOldColor;
    private int mCenterNewColor;
    private float mTranslationOffset;
    private RectF mColorWheelRectangle = new RectF();
    private RectF mCenterRectangle;
    private int mColorCenterRadius;
    private int mPreferredColorCenterRadius;
    private int mColorCenterHaloRadius;
    private int mPreferredColorCenterHaloRadius;
    private int mColorPointerHaloRadius;
    private float mSlopX;
    private float mSlopY;
    private boolean isUserIsMovingPointer = false;
    private int oldSelectedListenerColor;
    private OnColorChangedListener onColorChangedListener;
    private int oldChangedListenerColor;
    private int widthPm;
    private int heightPm;

    /**
     * 构造函数
     *
     * @param context context
     * @param attrs   attrs
     */
    public ColorPicker(Context context, AttrSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    /**
     * 初始化
     *
     * @param attrs attrs
     */
    public void init(AttrSet attrs) {
        HiLog.info(LABEL_LOG, attrs.toString());
        setTouchEventListener(this);
        Optional<Display>
                display = DisplayManager.getInstance().getDefaultDisplay(this.getContext());
        DisplayAttributes displayAttributes = display.get().getAttributes();
        display.isPresent();
        widthPm = displayAttributes.width;
        heightPm = displayAttributes.height;

        mColorWheelRadius = Constant.NUMBER124;
        mPreferredColorWheelRadius = mColorWheelRadius;
        mColorCenterRadius = Constant.NUMBER54;
        mPreferredColorCenterRadius = mColorCenterRadius;
        mColorCenterHaloRadius = Constant.NUMBER60;
        mPreferredColorCenterHaloRadius = mColorCenterHaloRadius;
        mColorPointerHaloRadius = Constant.NUMBER60;
        mAngle = (float) (-Math.PI / Constant.NUMBER2);

        // 梯形
        Color[] colors = {
                new Color(Color.rgb(Constant.NUMBER255, 0, 0)),
                new Color(Color.rgb(Constant.NUMBER255, 0, Constant.NUMBER255)),
                new Color(Color.rgb(0, 0, Constant.NUMBER255)),
                new Color(Color.rgb(0, Constant.NUMBER255, Constant.NUMBER255)),
                new Color(Color.rgb(0, Constant.NUMBER255, 0)),
                new Color(Color.rgb(Constant.NUMBER255, Constant.NUMBER255, 0)),
                new Color(Color.rgb(Constant.NUMBER255, 0, 0)),
        };
        float[] mGradientPosition = new float[]{
                0.0f, Constant.NUMBER01F, Constant.NUMBER02F, Constant.NUMBER03F, Constant.NUMBER04F,
                Constant.NUMBER05F, Constant.NUMBER06F, Constant.NUMBER07F, Constant.NUMBER08F,
                Constant.NUMBER09F, 1.0f};
        Shader shader = new SweepShader(0, 0, colors, mGradientPosition);
        mColorWheelPaint = new Paint();
        mColorWheelPaint.setShader(shader, Paint.ShaderType.SWEEP_SHADER);
        mColorWheelPaint.setStyle(Paint.Style.STROKE_STYLE);
        mColorWheelPaint.setStrokeWidth(Constant.NUMBER24);
        initDate();
    }

    /**
     * 初始化
     */
    public void initDate() {
        mPointerHaloPaint = new Paint();
        mPointerHaloPaint.setColor(Color.BLACK);
        mPointerHaloPaint.setAlpha(Constant.NUMBER04F);

        mPointerColor = new Paint();
        mPointerColor.setColor(new Color(calculateColor(mAngle)));

        mCenterNewPaint = new Paint();
        mCenterNewPaint.setColor(new Color(calculateColor(mAngle)));
        mCenterNewPaint.setStyle(Paint.Style.FILL_STYLE);

        mCenterOldPaint = new Paint();
        mCenterOldPaint.setColor(new Color(calculateColor(mAngle)));
        mCenterOldPaint.setStyle(Paint.Style.FILL_STYLE);

        mCenterHaloPaint = new Paint();
        mCenterHaloPaint.setColor(Color.BLACK);
        mCenterHaloPaint.setAlpha(0x00);

        mCenterNewColor = calculateColor(mAngle);
        mCenterOldColor = calculateColor(mAngle);

        int min = Math.min(widthPm, heightPm);
        mTranslationOffset = min * Constant.NUMBER05F;

        // fill the rectangle instances.
        mColorWheelRadius = min / Constant.NUMBER3;
        mColorWheelRectangle = new RectF(-mColorWheelRadius, -mColorWheelRadius,
                mColorWheelRadius, mColorWheelRadius);

        mColorCenterRadius = (int) ((float) mPreferredColorCenterRadius * ((float) mColorWheelRadius
                / (float) mPreferredColorWheelRadius));
        mColorCenterHaloRadius = (int) ((float) mPreferredColorCenterHaloRadius * ((float) mColorWheelRadius
                / (float) mPreferredColorWheelRadius));
        mCenterRectangle = new RectF(-mColorCenterRadius, -mColorCenterRadius,
                mColorCenterRadius, mColorCenterRadius);
        isShowCenterOldColor = false;
        invalidate();
        addDrawTask(this);
    }

    private int calculateColor(float angle) {
        float unit = (float) (angle / (Constant.NUMBER2 * Math.PI));
        if (unit < 0) {
            unit += 1;
        }

        if (unit <= 0) {
            return COLORS[0];
        }
        if (unit >= 1) {
            return COLORS[COLORS.length - 1];
        }

        float pp = unit * (COLORS.length - 1);
        int ii = (int) pp;
        pp -= ii;

        int c0 = COLORS[ii];
        int c1 = COLORS[ii + 1];
        int aa = ave(Color.alpha(c0), Color.alpha(c1), pp);

        int[] c0rgb = colorToRgb(c0);
        int[] c1rgb = colorToRgb(c1);

        int rr = ave(c0rgb[0], c1rgb[0], pp);
        int gg = ave(c0rgb[1], c1rgb[1], pp);
        int bb = ave(c0rgb[Constant.NUMBER2], c1rgb[Constant.NUMBER2], pp);

        return Color.argb(aa, rr, gg, bb);
    }

    private int ave(int ss, int dd, float pp) {
        return ss + Math.round(pp * (dd - ss));
    }

    /**
     * onDraw
     *
     * @param component component
     * @param canvas    canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        canvas.translate(mTranslationOffset, mTranslationOffset);

        // Draw the color wheel.
        canvas.drawOval(mColorWheelRectangle, mColorWheelPaint);

        float[] pointerPosition = calculatePointerPosition(mAngle);
        canvas.drawCircle(pointerPosition[0], pointerPosition[1],
                Constant.NUMBER54, mPointerHaloPaint);
        canvas.drawCircle(pointerPosition[0], pointerPosition[1],
                Constant.NUMBER42, mPointerColor);

        canvas.drawCircle(0, 0, mColorCenterHaloRadius, mCenterHaloPaint);
        if (isShowCenterOldColor) {
            // Draw the old selected color in the center.
            canvas.drawArc(mCenterRectangle, new Arc(Constant.NUMBER90, Constant.NUMBER180,
                    true), mCenterOldPaint);

            // Draw the new selected color in the center.
            canvas.drawArc(mCenterRectangle, new Arc(Constant.NUMBER270, Constant.NUMBER180,
                    true), mCenterNewPaint);
        } else {
            // Draw the new selected color in the center.
            canvas.drawArc(mCenterRectangle, new Arc(0, Constant.NUMBER360, true), mCenterNewPaint);
        }
    }

    /**
     * onTouchEvent
     *
     * @param component component
     * @param event     event
     * @return onTouchEvent
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
        // Convert coordinates to our internal coordinate system
        float xx = event.getPointerPosition(event.getIndex()).getX() - mTranslationOffset;
        float yy = event.getPointerPosition(event.getIndex()).getY() - mTranslationOffset;

        switch (event.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                // Check whether the user pressed on the pointer.
                setDown(xx, yy);
                break;
            case TouchEvent.POINT_MOVE:
                if (isUserIsMovingPointer) {
                    mAngle = (float) Math.atan2(yy - mSlopY, xx - mSlopX);
                    mPointerColor.setColor(new Color(calculateColor(mAngle)));
                    mCenterNewColor = calculateColor(mAngle);
                    setNewCenterColor(mCenterNewColor);
                    invalidate();
                }

                // If user did not press pointer or center, report event not handled
                else {
                    return false;
                }
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                isUserIsMovingPointer = false;
                mCenterHaloPaint.setAlpha(0x00);

                if (onColorSelectedListener != null && mCenterNewColor != oldSelectedListenerColor) {
                    onColorSelectedListener.onColorSelected(mCenterNewColor);
                    oldSelectedListenerColor = mCenterNewColor;
                }

                invalidate();
                break;
            case TouchEvent.PHASE_CANCEL:
                if (onColorSelectedListener != null && mCenterNewColor != oldSelectedListenerColor) {
                    onColorSelectedListener.onColorSelected(mCenterNewColor);
                    oldSelectedListenerColor = mCenterNewColor;
                }
                break;
            default:
        }
        return true;
    }

    /**
     * 触摸事件的按下去
     *
     * @param xx xx
     * @param yy yy
     */
    public void setDown(float xx, float yy) {
        float[] pointerPosition = calculatePointerPosition(mAngle);
        if (xx >= (pointerPosition[0] - mColorPointerHaloRadius)
                && xx <= (pointerPosition[0] + mColorPointerHaloRadius)
                && yy >= (pointerPosition[1] - mColorPointerHaloRadius)
                && yy <= (pointerPosition[1] + mColorPointerHaloRadius)) {
            mSlopX = xx - pointerPosition[0];
            mSlopY = yy - pointerPosition[1];
            isUserIsMovingPointer = true;
            invalidate();
        }

        // Check whether the user pressed on the center.
        else if (xx >= -mColorCenterRadius && xx <= mColorCenterRadius
                && yy >= -mColorCenterRadius && yy <= mColorCenterRadius
                && isShowCenterOldColor) {
            mCenterHaloPaint.setAlpha(Constant.COLOR0X50);
            invalidate();
        }

        // Check whether the user pressed anywhere on the wheel.
        else if (Math.sqrt(xx * xx + yy * yy) <= mColorWheelRadius + mColorPointerHaloRadius
                && Math.sqrt(xx * xx + yy * yy) >= mColorWheelRadius
                - mColorPointerHaloRadius) {
            isUserIsMovingPointer = true;
            invalidate();
        }

        // If user did not press pointer or center, report event not handled
        else {
            HiLog.info(LABEL_LOG, "CONTENT");
        }
    }

    private float[] calculatePointerPosition(float angle) {
        float xx = (float) (mColorWheelRadius * Math.cos(angle));
        float yy = (float) (mColorWheelRadius * Math.sin(angle));

        return new float[]{xx, yy};
    }

    /**
     * 设置颜色值
     *
     * @param color color
     */
    public void setNewCenterColor(int color) {
        mCenterNewColor = color;
        mCenterNewPaint.setColor(new Color(color));
        if (mCenterOldColor == 0) {
            mCenterOldColor = color;
            mCenterOldPaint.setColor(new Color(color));
        }
        if (onColorChangedListener != null && color != oldChangedListenerColor) {
            onColorChangedListener.onColorChanged(color);
            oldChangedListenerColor = color;
        }
        invalidate();
    }

    /**
     * getOnColorChangedListener
     *
     * @return getOnColorChangedListener
     */
    public OnColorChangedListener getOnColorChangedListener() {
        return this.onColorChangedListener;
    }

    /**
     * setOnColorChangedListener
     *
     * @param listener listener
     */
    public void setOnColorChangedListener(OnColorChangedListener listener) {
        this.onColorChangedListener = listener;
    }

    /**
     * OnColorSelectedListener
     *
     * @author VoiceRipple
     * @since 2021-04-16
     */
    public interface OnColorSelectedListener {
        /**
         * onColorSelected
         *
         * @param color color
         */
        void onColorSelected(int color);
    }

    /**
     * OnColorChangedListener
     *
     * @author VoiceRipple
     * @since 2021-04-16
     */
    public interface OnColorChangedListener {
        /**
         * onColorChanged
         *
         * @param color color
         */
        void onColorChanged(int color);
    }
}
